/*
 * Project: Hell Watch
 * Author: pajoke
 * Copyright: (C) 2014 Hell Prototypes
 * License: GNU GPL v3 (see License.txt)
 * Web: http://www.hellprototypes.com
 */


#include <avr/io.h>
#include <string.h>
#include <avr/interrupt.h>
#include "common.h"
#include "devices/PCF8563.h"
#include "devices/PCF8563_defs.h"
#include "drivers/i2c.h"
#include "millis/millis.h"

void PCF8563_timer_start(void);
static bool read(byte, byte*, byte);
static bool write(byte, byte);

void PCF8563_init()
{
	byte data;

	if(!read(PCF8563ADDR_CTRL_1, &data, 1)) {
		return;
	}
	if(data != 0x00) {
		if(!write(PCF8563ADDR_CTRL_1, 0x00)) {
			return;
		}
	}

	if(!read(PCF8563ADDR_CLKOUT_CTRL, &data, 1)) {
		return;
	}
	if(data & 0x80) {
		if(!write(PCF8563ADDR_CLKOUT_CTRL, 0x00)) {
			return;
		}
	}

	if(!read(PCF8563ADDR_SECS, &data, 1)) {
		return;
	}
	if(data & 0x80) {
		write(PCF8563ADDR_SECS, data & 0x7F);
	}

	if(!read(PCF8563ADDR_CTRL_2, &data, 1)){
		return;
	}
	data |= 0x01;
	if(!write(PCF8563ADDR_CTRL_2, data)) {
		return;
	}

	PORTA.PIN5CTRL	= 0x18 | 0x00;//PIN_RTC_ACC_INT - pull up, low level
	PORTA.INTCTRL  |= 0x04;

	PCF8563_timer_start();
}

void PCF8563_timer_start(void)
{
	if(!write(PCF8563ADDR_TIMER_CTRL, 0x83)) { //0x81:4096Hz, 0x81:64Hz, 0x82: 1Hz, 0x83: 1/60Hz
		return;
	}
	write(PCF8563ADDR_TIMER, 60);// 1 hour
}

// Get time
void PCF8563_get(s_time* timeData)
{
	read(PCF8563ADDR_SECS, (byte*)timeData, sizeof(s_time));

	//Mask unused bits
	//timeData->secs  = timeData->secs & 0x7f;
	timeData->mins  = timeData->mins & 0x7f;
	timeData->hours = timeData->hours& 0x3f;
	timeData->date  = timeData->date & 0x3f;
	timeData->day   = timeData->day  & 0x07;
	timeData->month = timeData->month& 0x1f;
	//timeData->year  = timeData->year;
}

// Save time
void PCF8563_save(s_time* timeData)
{
	// Month is stored starting at 0 in program, but RTC starts at 1
	byte month = timeData->month + 1;
	if(month == 0b00001010) // invalid BCD, added 1 to 9 = 10 = bad
		month = 0b00010000; // add 6 to fix, but that increases program size, so just set manually

	write(PCF8563ADDR_SECS,	timeData->secs);
	write(PCF8563ADDR_MINS,	timeData->mins);
	write(PCF8563ADDR_HRS,		timeData->hours);
	write(PCF8563ADDR_DAY,		timeData->day);
	write(PCF8563ADDR_DATE,	timeData->date);
	write(PCF8563ADDR_MONTH,	month);
	write(PCF8563ADDR_YEAR,	timeData->year);
}

// User alarm uses alarm 1
void PCF8563_setUserAlarmWake(s_alarm* alarm)
{
	// Clear flag
	PCF8563_userAlarmState();

	byte data;
	read(PCF8563ADDR_CTRL_2, &data, 1);
	if(alarm != NULL)
	{
		write(PCF8563ADDR_ALARM1_MINS,	alarm->min);
		write(PCF8563ADDR_ALARM1_HRS,	alarm->hour);
		write(PCF8563ADDR_ALARM1_DAY,  alarm->days);
		data |= 0x02;
	}
	else // Alarm disabled
		data &= ~0x02;
	write(PCF8563ADDR_CTRL_2, data);
}

// System wakeup uses alarm 2
// NOTE: Alarm 2 doesn't have seconds
void PCF8563_setSystemAlarmWake(void)
{
	//if need: Clear flag and restart
	PCF8563_systemAlarmState();

	PORTA.INT1MASK |= 0x20;//RTC_ACC_INT at PA5
}

// Get user alarm state
bool PCF8563_userAlarmState()
{
	byte data;

	read(PCF8563ADDR_CTRL_2, &data, 1);
	bool state = data & 0x08;
	if(state) // Clear state if its set
	{
		write(PCF8563ADDR_CTRL_2, data & (~0x08));
	}

	return state;
}

// Get systel alarm state
bool PCF8563_systemAlarmState()
{
	if(!(PORT_CTRL_IN & 0x20)) {
		byte data;
		read(PCF8563ADDR_CTRL_2, &data, 1);
		bool state = data & 0x04;
		if(state) // Clear state if its set
		{
			PCF8563_timer_start();
			write(PCF8563ADDR_CTRL_2, data & (~0x04));
			return true;
		} else {
			return false;
		}
	}
	return false;
}

// I2C read
static bool read(byte address, byte* data, byte count)
{
	return i2c_dev_read(PCF8563I2C_W, address, data, count);
}

// I2C write
static bool write(byte address, byte data)
{
	return i2c_dev_write(PCF8563I2C_W, address, data);
}

ISR(PORTA_INT1_vect)
{
	PORTA.INT1MASK &= ~0x20;//RTC_ACC_INT at PA5
	//printf("ISR\r\n");//FIXME
	//if(!(PORT_CTRL_IN & 0x20)) {
	DBG_TRIGGER();
	//}
}

